package com.huawang.mdesign.model.service.util.mindmap;

import cn.hutool.core.util.ZipUtil;
import com.huawang.mdesign.model.service.constant.mindmap.MindMapEnum;
import com.huawang.mdesign.model.service.util.mindmap.xmind.Item;
import com.huawang.mdesign.model.service.util.mindmap.xmind.ItemAttached;
import com.huawang.mdesign.model.service.util.mindmap.xmind.Root;
import lombok.SneakyThrows;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.File;
import java.util.*;

/**
 * MindManager解析类
 *
 * @author admin
 */
@Component
public class MindManagerUtil implements MindMap {

    public static final String AP_ONE_TOPIC = "ap:OneTopic";

    public static final String AP_TOPIC = "ap:Topic";

    public static final String AP_TEXT = "ap:Text";

    public static final String PLAIN_TEXT = "PlainText";

    public static final String AP_SUBTOPICS = "ap:SubTopics";

    public static final String AP_STYLE_GROUP = "ap:StyleGroup";

    public static final String AP_ROOT_TOPIC_DEFAULT_GROUP = "ap:RootTopicDefaultsGroup";

    public static final String AP_DEFAULT_TEXT = "ap:DefaultText";

    public static final String AP_ROOT_SUBTOPIC_DEFAULT_GROUP = "ap:RootSubTopicDefaultsGroup";

    public static final String OID = "OId";

    @Override
    public boolean support(@NotNull String sourceFile) {
        return StringUtils.isNotBlank(sourceFile) && sourceFile.endsWith(MindMapEnum.MIND_MANAGER.getSuffix());
    }

    @Override
    public @NotNull List<Root> read(@NotNull String sourceFile) {
        return parse(sourceFile);
    }

    /**
     * 解析MindManager源文件
     *
     * @param sourceFile 源文件
     * @return 树型结构
     */
    @SneakyThrows
    @NotNull
    public static List<Root> parse(@NotNull String sourceFile) {
        //先解压
        File source = new File(sourceFile);
        if (!source.exists() || !source.isFile() || !sourceFile.endsWith(MindMapEnum.MIND_MANAGER.getSuffix())) {
            return Collections.emptyList();
        }

        //add prefix
        File content = XMindUtil.getContent(ZipUtil.unzip(source), MindMapEnum.MIND_MANAGER.getTargetFile());
        Document doc = FreeMindUtil.parseXml(content.getAbsolutePath());

        NodeList nodes = doc.getElementsByTagName(AP_ONE_TOPIC);
        Item root = new Item();
        Set<String> ids = new HashSet<>();
        for (int i = 0, len = nodes.getLength(); i < len; ++i) {
            getChildren(nodes.item(i).getChildNodes(), root, ids);
        }

        nodes = doc.getElementsByTagName(AP_STYLE_GROUP);
        for (int i = 0, len = nodes.getLength(); i < len; ++i) {
            getChildren(nodes.item(i).getChildNodes(), root);
        }
        return FreeMindUtil.root2List(root);
    }

    /**
     * 获取子节点
     *
     * @param nodes  当前节点
     * @param parent 父节点
     */
    private static void getChildren(@NotNull NodeList nodes, @Nullable Item parent) {
        Item root = null;
        for (int i = 0, len = nodes.getLength(); i < len; ++i) {
            Node node = nodes.item(i);
            if (Objects.equals(node.getNodeName(), AP_ROOT_TOPIC_DEFAULT_GROUP)) {
                root = Optional.ofNullable(parent)
                        .map(Item::getChildren)
                        .map(ItemAttached::getAttached)
                        .filter(CollectionUtils::isNotEmpty)
                        .map(e -> e.get(0))
                        .orElse(null);

                Optional.ofNullable(root)
                        .ifPresent(e -> e.setTitle(getTitle(node, AP_DEFAULT_TEXT)));
            } else if (Objects.equals(node.getNodeName(), AP_ROOT_SUBTOPIC_DEFAULT_GROUP)) {
                setTitle(root, getTitle(node, AP_DEFAULT_TEXT));
            }
        }
    }

    /**
     * 获取标题
     *
     * @param node 当前节点
     * @param tag  标签
     * @return 标题名
     */
    @NotNull
    private static String getTitle(@Nullable Node node, @Nullable String tag) {
        return Optional.ofNullable(node)
                .map(e -> getChildByTagName(e, tag))
                .filter(Element.class::isInstance)
                .map(Element.class::cast)
                .map(e -> e.getAttribute(PLAIN_TEXT))
                .orElse(Strings.EMPTY);
    }

    /**
     * 设置标题
     *
     * @param root  当前节点
     * @param title 标题
     */
    private static void setTitle(@Nullable Item root, @Nullable String title) {
        Optional.ofNullable(root)
                .map(Item::getChildren)
                .map(ItemAttached::getAttached)
                .filter(CollectionUtils::isNotEmpty)
                .ifPresent(e -> {
                    for (Item k : e) {
                        if (StringUtils.isNotBlank(k.getId()) && StringUtils.isBlank(k.getTitle())) {
                            k.setTitle(title);
                            break;
                        } else {
                            setTitle(k, title);
                        }
                    }
                });
    }

    /**
     * 解析子节点
     *
     * @param nodes  当前节点
     * @param parent 父节点
     * @param ids    节点集合
     */
    private static void getChildren(@NotNull NodeList nodes, @Nullable Item parent, @NotNull Set<String> ids) {
        List<Item> children = new ArrayList<>();
        for (int i = 0, len = nodes.getLength(); i < len; ++i) {
            Node node = nodes.item(i);
            if (Objects.equals(node.getNodeName(), AP_TOPIC)) {
                String id = Optional.of(node)
                        .filter(Element.class::isInstance)
                        .map(Element.class::cast)
                        .map(e -> e.getAttribute(OID))
                        .orElse(Strings.EMPTY);

                if (StringUtils.isBlank(id) || ids.contains(id)) {
                    continue;
                }
                ids.add(id);

                Item item = new Item();
                item.setId(id);
                item.setTitle(getTitle(node, AP_TEXT));
                children.add(item);

                Optional.of(node)
                        .map(e -> getChildByTagName(e, AP_SUBTOPICS))
                        .map(Node::getChildNodes)
                        .ifPresent(e -> getChildren(e, item, ids));
            }
        }

        FreeMindUtil.setChildren(children, parent);
    }

    /**
     * 根据标签名获取子节点
     *
     * @param node 父节点
     * @param tag  标签名
     * @return 子节点
     */
    @Nullable
    public static Node getChildByTagName(@Nullable Node node, @Nullable String tag) {
        if (Objects.nonNull(node)) {
            NodeList childNodes = node.getChildNodes();
            for (int i = 0, len = childNodes.getLength(); i < len; ++i) {
                Node item = childNodes.item(i);
                if (Objects.equals(item.getNodeName(), tag)) {
                    return item;
                }
            }
        }
        return null;
    }
}
